1 /***
2 * Redistribution and use of this software and associated documentation
3 * ("Software"), with or without modification, are permitted provided
4 * that the following conditions are met:
5 *
6 * 1. Redistributions of source code must retain copyright
7 * statements and notices. Redistributions must also contain a
8 * copy of this document.
9 *
10 * 2. Redistributions in binary form must reproduce the
11 * above copyright notice, this list of conditions and the
12 * following disclaimer in the documentation and/or other
13 * materials provided with the distribution.
14 *
15 * 3. The name "Exolab" must not be used to endorse or promote
16 * products derived from this Software without prior written
17 * permission of Exoffice Technologies. For written permission,
18 * please contact info@exolab.org.
19 *
20 * 4. Products derived from this Software may not be called "Exolab"
21 * nor may "Exolab" appear in their names without prior written
22 * permission of Exoffice Technologies. Exolab is a registered
23 * trademark of Exoffice Technologies.
24 *
25 * 5. Due credit should be given to the Exolab Project
26 * (http://www.exolab.org/).
27 *
28 * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
32 * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39 * OF THE POSSIBILITY OF SUCH DAMAGE.
40 *
41 * Copyright 2000-2005 (C) Exoffice Technologies Inc. All Rights Reserved.
42 */
43 package org.exolab.jms.persistence;
44
45 import java.sql.Connection;
46 import java.sql.PreparedStatement;
47 import java.sql.ResultSet;
48 import java.util.HashMap;
49 import java.util.Iterator;
50 import java.util.Vector;
51
52 import org.exolab.jms.client.JmsDestination;
53 import org.exolab.jms.client.JmsQueue;
54 import org.exolab.jms.client.JmsTopic;
55
56
57 /***
58 * This class provides persistency for JmsDestination objects in an RDBMS
59 * database.
60 *
61 * @author <a href="mailto:tma@netspace.net.au">Tim Anderson</a>
62 * @version $Revision: 1.4 $ $Date: 2005/08/31 05:45:50 $
63 */
64 class Destinations {
65
66 /***
67 * The seed generator.
68 */
69 private final SeedGenerator _seeds;
70
71 /***
72 * The consumer manager.
73 */
74 private final Consumers _consumers;
75
76 /***
77 * Cache of all destinations indexed on names.
78 */
79 private final HashMap _destinations = new HashMap();
80
81 /***
82 * Cache of all destinations indexed on identity.
83 */
84 private final HashMap _ids = new HashMap();
85
86 /***
87 * This is the name of the destination id generator, which uniquely created
88 * identities for destinations.
89 */
90 private static final String DESTINATION_ID_SEED = "destinationId";
91
92 /***
93 * Create a new <code>Destinations</code>.
94 *
95 * @param seeds the seed manager
96 * @param consumers the consumer manager
97 * @param connection the connection to initialise from
98 * @throws PersistenceException if initialisation fails
99 */
100 public Destinations(SeedGenerator seeds,
101 Consumers consumers,
102 Connection connection) throws PersistenceException {
103 _seeds = seeds;
104 _consumers = consumers;
105 load(connection);
106 }
107
108 /***
109 * Add a new destination to the database. This method will also assign it a
110 * unique identity.
111 *
112 * @param connection the connection to use.
113 * @param destination the destination to add
114 * @throws PersistenceException if the destination cannot be added
115 */
116 public synchronized void add(Connection connection,
117 JmsDestination destination)
118 throws PersistenceException {
119
120 PreparedStatement insert = null;
121 try {
122 long Id = _seeds.next(connection, DESTINATION_ID_SEED);
123 boolean isQueue = (destination instanceof JmsQueue);
124
125 insert = connection.prepareStatement(
126 "insert into destinations (name, isqueue, destinationid) "
127 + "values (?, ?, ?)");
128 insert.setString(1, destination.getName());
129 insert.setBoolean(2, isQueue);
130 insert.setLong(3, Id);
131 insert.executeUpdate();
132 cache(destination, Id);
133 } catch (Exception error) {
134 throw new PersistenceException("Destinations.add failed with " +
135 error.toString());
136 } finally {
137 SQLHelper.close(insert);
138 }
139 }
140
141 /***
142 * Remove a destination from the database. This removes all associated
143 * consumers, and messages.
144 *
145 * @param connection the connection to use
146 * @param destination the destination
147 * @return <code>true</cpde> if it was removed
148 * @throws PersistenceException if the request fails
149 */
150 public synchronized boolean remove(Connection connection,
151 JmsDestination destination)
152 throws PersistenceException {
153
154 boolean success = false;
155 PreparedStatement deleteDestinations = null;
156 PreparedStatement deleteMessages = null;
157 PreparedStatement deleteConsumers = null;
158 PreparedStatement deleteMessageHandles = null;
159
160 Pair pair = (Pair) _destinations.get(destination.getName());
161 if (pair != null) {
162 try {
163 deleteDestinations = connection.prepareStatement(
164 "delete from destinations where name=?");
165 deleteDestinations.setString(1, destination.getName());
166
167 deleteMessages = connection.prepareStatement(
168 "delete from messages where destinationId=?");
169 deleteMessages.setLong(1, pair.Id);
170
171 deleteMessageHandles = connection.prepareStatement(
172 "delete from message_handles where destinationId=?");
173 deleteMessageHandles.setLong(1, pair.Id);
174
175 deleteConsumers = connection.prepareStatement(
176 "delete from consumers where destinationId=?");
177 deleteConsumers.setLong(1, pair.Id);
178
179
180 deleteDestinations.executeUpdate();
181 deleteMessages.executeUpdate();
182 deleteMessageHandles.executeUpdate();
183 deleteConsumers.executeUpdate();
184
185 _consumers.removeCached(pair.Id);
186 _destinations.remove(destination.getName());
187 _ids.remove(new Long(pair.Id));
188 success = true;
189 } catch (Exception error) {
190 throw new PersistenceException("Failed to remove destination",
191 error);
192 } finally {
193 SQLHelper.close(deleteDestinations);
194 SQLHelper.close(deleteMessages);
195 SQLHelper.close(deleteConsumers);
196 SQLHelper.close(deleteMessageHandles);
197 }
198 }
199
200 return success;
201 }
202
203 /***
204 * Returns the destination associated with name.
205 *
206 * @param name the name of the destination
207 * @return the destination, or null
208 */
209 public synchronized JmsDestination get(String name) {
210 Pair pair = (Pair) _destinations.get(name);
211 return (pair != null) ? pair.destination : null;
212 }
213
214 /***
215 * Returns the destination associated with Id.
216 *
217 * @param id the destination Id
218 * @return the destination or null
219 */
220 public synchronized JmsDestination get(long id) {
221 Pair pair = (Pair) _ids.get(new Long(id));
222 return (pair != null) ? pair.destination : null;
223 }
224
225 /***
226 * Returns the Id for a given destination name
227 *
228 * @param name the destination name
229 * @return the destination Id, or 0 if it doesn't exist
230 */
231 public synchronized long getId(String name) {
232 Pair pair = (Pair) _destinations.get(name);
233 return (pair != null) ? pair.Id : 0;
234 }
235
236 /***
237 * Returns the list of destination names.
238 *
239 * @return list of destination names
240 */
241 public synchronized Vector getNames() {
242
243 Vector result = new Vector(_destinations.size());
244 Iterator iter = _destinations.keySet().iterator();
245 while (iter.hasNext()) {
246 result.add((String) iter.next());
247 }
248
249 return result;
250 }
251
252 /***
253 * Returns the list of destination objects.
254 *
255 * @return list of destination objects
256 */
257 public synchronized Vector getDestinations() {
258
259 Vector result = new Vector(_destinations.size());
260 Iterator iter = _destinations.values().iterator();
261 while (iter.hasNext()) {
262 result.add(((Pair) iter.next()).destination);
263 }
264
265 return result;
266 }
267
268 /***
269 * Deallocates resources owned or referenced by the instance.
270 */
271 public synchronized void close() {
272 _destinations.clear();
273 _ids.clear();
274 }
275
276 /***
277 * Load all the destinations in memory. It uses the transaction service and
278 * the database service to access the appropriate resources.
279 *
280 * @param connection the connection to use
281 * @throws PersistenceException if the
282 */
283 private void load(Connection connection)
284 throws PersistenceException {
285
286 PreparedStatement select = null;
287 ResultSet set = null;
288 try {
289 select = connection.prepareStatement(
290 "select name, isqueue, destinationid from destinations");
291
292 set = select.executeQuery();
293 String name = null;
294 boolean isQueue = false;
295 JmsDestination destination = null;
296 long Id = 0;
297 while (set.next()) {
298 name = set.getString(1);
299 isQueue = set.getBoolean(2);
300 destination = (isQueue)
301 ? (JmsDestination) new JmsQueue(name)
302 : (JmsDestination) new JmsTopic(name);
303 Id = set.getLong(3);
304 destination.setPersistent(true);
305 cache(destination, Id);
306 }
307 } catch (Exception exception) {
308 throw new PersistenceException("Failed to load destinations",
309 exception);
310 } finally {
311 SQLHelper.close(set);
312 SQLHelper.close(select);
313 }
314 }
315
316 /***
317 * Cache a destination.
318 *
319 * @param destination the destination to cache
320 * @param Id the destination identity
321 */
322 private void cache(JmsDestination destination, long Id) {
323 Pair pair = new Pair(destination, Id);
324
325 _destinations.put(destination.getName(), pair);
326 _ids.put(new Long(Id), pair);
327 }
328
329
330 /***
331 * This private static class holds the name and identity of the destination
332 */
333 private static class Pair {
334
335 public Pair(JmsDestination destination, long Id) {
336 this.destination = destination;
337 this.Id = Id;
338 }
339
340 public JmsDestination destination;
341 public long Id;
342 }
343 }